<?php
/*********************************************
 *  CPG Dragonfly™ CMS
 *********************************************
	Copyright © since 2010 by CPG-Nuke Dev Team
	http://dragonflycms.org

	Dragonfly is released under the terms and conditions
	of the GNU GPL version 2 or any later version

	$Source$
	$Revision$
	$Author$
	$Date$
**********************************************/

#
# The core classes
#

# Advanced "on call" factory to prevent server overloading
# if a class isn't always needed
class Poodle_Handler
{
	private
		$class,
		$class_inst = null;

	function __construct($class_name, $owner, $owner_property)
	{
		$this->class = array($class_name, $owner, $owner_property);
	}

	function __call($method, $args)
	{
		$c = $this->load_inst();
		switch (count($args))
		{
		case 0: return $c->$method();
		case 1: return $c->$method($args[0]);
		case 2: return $c->$method($args[0], $args[1]);
		}
		return call_user_func_array(array($c, $method), $args); # this is slow
	}
	function __get($key) { return $this->load_inst()->$key; }
	function __set($key, $val) { $this->load_inst(false)->$key = $val; /* child pid N exit signal Segmentation fault (11) */ }

	# Check if already loaded else load it into memory
	private function load_inst($push=true)
	{
		if (!$this->class_inst) $this->class_inst = new $this->class[0]($this);
		if ($push) $this->class[1]->{$this->class[2]} = $this->class_inst;
		return $this->class_inst;
	}
}

class Poodle_Readonly extends Poodle
{
	protected $_readonly_data = array(); # Read-only properties
	function __construct($data) { $this->_readonly_data = $data; }
	function __get($key)
	{
		if (isset($this->_readonly_data[$key])) { return $this->_readonly_data[$key]; }
		$tmp = debug_backtrace();
		trigger_error("Undefined property: {$key} by: {$tmp[0]['file']}#{$tmp[0]['line']}");
		return null;
	}
	function __set($key, $val)
	{
		if (array_key_exists($key, $this->_readonly_data)) {
			throw new Exception('Disallowed to set property: '.$key);
		}
		$this->$key = $val;
	}
	function __isset($key) { return isset($this->_readonly_data[$key]); }
	function __unset($key) { throw new Exception('Disallowed to unset property: '.$key); }
	public function get_ro() { return $this->_readonly_data; }
	public function add_ro($key, $val)
	{
		if (array_key_exists($key, $this->_readonly_data)) { throw new Exception('Disallowed to set property: '.$key); }
		$this->_readonly_data[$key] = $val;
	}
}

class Poodle_Kernels_Dragonfly extends Poodle_Readonly implements Poodle_Events_Interface
{
	/* PHP 5.4: php.net/traits
	use Poodle_Events;
	*/

	public
		$mlf      = 'html',

//		$CFG      = null,
//		$L10N     = null,
//		$OUT      = null,
		$RESOURCE = null,
		$SESSION  = null,
//		$SQL      = null,

		$host,
		$path     = '/',
		$base_uri = '/',

		$cookie_domain = null,

		$domains  = null;

	protected
		$_readonly_data = array(
			'auth_realm'  => 'My Website',
			'cache_dir'   => null,
			'cache_uri'   => null,
			'design_mode' => false,
			'dbms' => array('adapter'=>'', 'tbl_prefix'=>'', 'master'=>array(), 'slave'=>array()),
		),

		$_poodle_events = array();

	private static $CACHE_URI;

	function __construct(array $cfg)
	{
		if (!$cfg)
		{
			Poodle_HTTP_Headers::setStatus(503);
			exit('The URI that you requested, is temporarily unavailable due to maintenance on the server.');
		}

		Poodle_Debugger::start();

		if (Poodle::$EXT) { $this->mlf = Poodle::$EXT; }
		if (!isset($cfg['dbms']['slave'])) { $cfg['dbms']['slave'] = array(); }
		$this->_readonly_data = array_merge($this->_readonly_data, $cfg);

		Poodle_PHP_INI::set('user_agent', 'Poodle WCMS/'.self::VERSION.' (PHP '.phpversion().'; '.PHP_SAPI.'; '.PHP_OS.')');
/*		if (Poodle::$DEBUG & Poodle::DBG_PHP) {
			Poodle_PHP_INI::set('docref_root', 'http://php.net/');
			Poodle_PHP_INI::set('html_errors', 1);
		}*/

		# Set server defaults
		$this->host = $_SERVER['HTTP_HOST'];
		$this->path = dirname($_SERVER['SCRIPT_NAME']);
		if (POODLE_BACKEND) { $this->path = dirname($this->path); }
		if (strlen($this->path)>1) $this->path .= '/';
		$this->base_uri  = $this->path;
		if (!Poodle_PHP_INI::enabled('allow_url_fopen', false) || Poodle_PHP_INI::enabled('allow_url_include'))
		{
			# Force allow_url_fopen=on and allow_url_include=off
			stream_wrapper_unregister('ftp');
			stream_wrapper_unregister('ftps');
			stream_wrapper_unregister('http');
			stream_wrapper_unregister('https');
			stream_wrapper_register('http',  'Poodle_Stream_Wrapper_HTTP');
			stream_wrapper_register('https', 'Poodle_Stream_Wrapper_HTTP');
		}

		self::$CACHE_URI = $cfg['cache_uri'];

		register_shutdown_function(array($this, 'onDestroy'));
	}

	function __destruct() { if (property_exists($this, '_readonly_data')) { self::onDestroy(); } }
	private static $destroyed = false;
	public function onDestroy()
	{
		if (!self::$destroyed) {
			// bugs.php.net/33595
			if (!isset($this->_poodle_events) || !is_array($this->_poodle_events)) {
				trigger_error('onDestroy() $this->_poodle_events is already gone');
			} else
			if (!empty($this->_poodle_events['destroy']))
			{
				try { $this->triggerEvent('destroy'); } catch (Exception $e) { } # skip
			}
/*
			try {
				if (isset($this->SESSION)) {
					$this->SESSION->close();
					unset($this->SESSION);
				} else {
					trigger_error('onDestroy() $this->SESSION is already gone');
				}
			} catch (Exception $e) { } # skip
*/
			foreach (array_keys(get_object_vars($this)) as $val) { $this->$val = null; }
			self::$destroyed = true;
		}
	}

	function __get($key)
	{
		if ('IDENTITY'===$key) {
			if (!isset($this->IDENTITY)) {
				$this->IDENTITY = Dragonfly_Identity::getCurrent();
			}
			return $this->IDENTITY;
		}

		if ('CACHE'===$key) {
			if (!isset($this->CACHE)) {
				$this->CACHE = Poodle_Cache::factory(self::$CACHE_URI);
			}
			return $this->CACHE;
		}

		if ('OUT'===$key) {
			if (!isset($this->OUT)) {
				$this->OUT = new Dragonfly_TPL_HTML();
			}
			return $this->OUT;
		}

		if ('L10N'===$key) {
			return $this->L10N = new Poodle_L10N();
		}

		if ('CFG'===$key) {
			return $this->CFG = Dragonfly_Config::load();
		}

		if ('SQL'===$key) {
			$this->connect_db();
			return $this->SQL;
		}

		if (isset($this->_readonly_data[$key])) { return $this->_readonly_data[$key]; }
		$tmp = debug_backtrace();
		trigger_error("Undefined property: {$key} by: {$tmp[0]['file']}#{$tmp[0]['line']}");
		return null;
	}

	function __isset($key)
	{
		return (property_exists($this, $key) && isset($this->$key)) || parent::__isset($key);
	}

	public function addEventListener($type, $function)
	{
		$type = strtolower($type);
		if (!is_callable($function)) { trigger_error('Function for Event '.$type.' is not callable in '.get_class($this)); }
		else if (!isset($this->_poodle_events[$type])) { $this->_poodle_events[$type] = array($function); }
		else if (!in_array($function, $this->_poodle_events[$type], true)) { $this->_poodle_events[$type][] = $function; }
	}

	public function dispatchEvent(Poodle_Event $event)
	{
		$event->target = $this;
		$type = strtolower($event->type);
		if (!empty($this->_poodle_events[$type])) {
			foreach ($this->_poodle_events[$type] as $callback) { call_user_func($callback, $event); }
/*				try { call_user_func($callback, $event); }
				catch (Exception $e) { Poodle_LOG::error(__CLASS__, $e->getMessage()."\n".$e->getTraceAsString()); }*/
		}
	}

	public function removeEventListener($type, $function)
	{
		$type = strtolower($type);
		if (empty($this->_poodle_events[$type])) { trigger_error('Event type '.$type.' is empty in '.get_class($this)); }
		else {
			$key = array_search($function, $this->_poodle_events[$type], true);
			if (false !== $key) { unset($this->_poodle_events[$type][$key]); }
		}
	}

	public function triggerEvent($type, array $properties=array())
	{
		$this->dispatchEvent(new Poodle_Event($type));
	}

	public function connect_db()
	{
		static $dbms = null;
		if (isset($this->SQL) && $this->SQL->ping()) { return false; }
		if (null === $dbms) {
			$dbms = $this->_readonly_data['dbms'];
			unset($this->_readonly_data['dbms']);
		}
		$this->SQL = new Poodle_SQL_Dragonfly($dbms['adapter'], $dbms['master'], $dbms['tbl_prefix'], $dbms['slave']);
		$this->SQL->debug = Poodle::$DEBUG & Poodle::DBG_SQL | Poodle::$DEBUG & Poodle::DBG_SQL_QUERIES;
//		if (!isset($this->SQL->TBL->config)) { Poodle_Report::error(503); }
	}

	# start output buffering
	public static function ob_start() {}

	public function setCookie($name, $value='', $expire=-1)
	{
		return setcookie($name, $value, $expire, $this->base_uri, $this->cookie_domain?$this->cookie_domain:null, false);
	}

	public function run()
	{
		# Load database
		$this->connect_db();
	}

	public static function shortFilePath($file)
	{
		static $paths;
		if (!$paths) { $paths = array_merge(array(BASEDIR), explode(PATH_SEPARATOR, preg_replace('#\.+'.PATH_SEPARATOR.'#', '', get_include_path()))); }
		return str_replace($paths, '', $file);
//		if (!$paths) { $paths = '#^('.str_replace(PATH_SEPARATOR,'|',get_include_path()).')#'; }
//		if (!$paths) { $paths = '#^('.implode('|',array_merge(array(Poodle::$DIR_BASE), explode(PATH_SEPARATOR, preg_replace('#\.+'.PATH_SEPARATOR.'#', '', get_include_path())))).')#'; }
//		return preg_replace(self::$re_paths, '', $file);
	}
}


class Poodle_L10N implements ArrayAccess
{
	function __get($key)
	{
		global $MAIN_CFG, $currentlang;
		if ('id' === $key) { return 0; }
		if ('lng' === $key) { return $currentlang; }
		if ('array' === $key) { return array(); }
		if ('multilingual' === $key) { return !empty($MAIN_CFG['global']['language']); }
		return $this->get($key);
	}
	public function get($var, $var2=null) { return $var2 ? $var2 : $var; }
	public function dbget($var) { return defined($var) ? constant($var): $var; }

	public function filesizeToHuman($size, $precision=2)
	{
		static $sizes = array('%s   B', '%s KiB', '%s MiB', '%s GiB', '%s TiB');
		if (!is_int($size) && !ctype_digit($size)) { return null; }
		$size = max($size, 0);
		$i = ($size > 0) ? floor(log($size, 1024)) : 0;
		if ($i>0) { $size /= pow(1024, $i); }
		else { $precision = 0; }
		return sprintf($sizes[$i], $this->round($size, max(0, $precision)));
	}

	# ArrayAccess
	public function offsetExists($k)  { return isset(self::$data[$this->lng][$k]); }
	public function offsetGet($k)     { return $this->__get($k); }
	public function offsetSet($k, $v) {}
	public function offsetUnset($k)   {}
}

abstract class Poodle_LOG
{
	const
		CREATE  = 1, # created data
		MODIFY  = 2, # edited data. NOTE: don't confuse with UPDATE
		DELETE  = 3, # removed data. TIP: place deleted data in message
		UPDATE  = 4, # like forum prune, to bring something 'up to date'
		MOVE    = 5, # moved data. TIP: place 'moved from' in message
		MERGE   = 6,
		SPLIT   = 7,
		APPROVE = 8,
		DENY    = 9,
		FLASH   = 10,
		LOGIN   = 11,
		SMS     = 12;
	public static function debug  ($type, $msg, $lh=false) { self::insert(1, $type, $msg, $lh); }
	public static function event  ($type, $msg, $lh=false) { self::insert(2, $type, $msg, $lh); }
	public static function notice ($type, $msg, $lh=false) { self::insert(3, $type, $msg, $lh); }
	public static function warning($type, $msg, $lh=false) { self::insert(4, $type, $msg, $lh); }
	public static function error  ($type, $msg, $lh=true)  { self::insert(5, $type, $msg, $lh); }
	protected static function insert($level, $type, $msg, $log_headers=false)
	{
		trigger_error("Dragonfly LOG: $type, $msg");
	}
}

class Poodle_Report
{

	public static function error($title, $error='', $redirect=false)
	{
		$message = is_array($error) ? $error['msg'] : $error;
		cpg_error($message, $title, $redirect);
	}

	public static function confirm($msg, $hidden='', $uri=false)
	{
	}
}
